#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BitwiseOre.SharedMemory.Core;
using BitwiseOre.SharedMemory.Core.Common;
using BitwiseOre.SharedMemory.Core.IPCQueues;
using NUnit.Framework;
using Moq;

namespace BitwiseOre.Tests.SharedMemory.Connection
{
    [TestFixture]
    public sealed class DisconnectorFixture : AssertionHelper
    {
        [Test(Description = "Disconnecting returns an unstarted task that exectures the disconnect operation.")]
        public void Disconnect_ReturnsUnstartedTask()
        {
            var disconnector = new Disconnector(CreateQueues(),
                                                _ => new Mock<ITimeoutHelper>().Object);
            var task = disconnector.Disconnect(TimeSpan.FromMinutes(1));

            Expect(task.Status, EqualTo(TaskStatus.Created), "Disconnect task was started.");
        }

        [Test(Description = "Disconnecting creates a timeout helper.")]
        public void Disconnect_CreatesTimeoutHelper()
        {
            var created = false;

            Func<TimeSpan, ITimeoutHelper> create = _ =>
                                                        {
                                                            created = true;
                                                            return new Mock<ITimeoutHelper>().Object;
                                                        };

            var disconnector = new Disconnector(CreateQueues(), create);
            disconnector.Disconnect(TimeSpan.FromMinutes(1));
            
            Expect(created, "No timeout helper was created.");
        }

        [Test(Description = "Disconnecting shuts down writes on the connection queues.")]
        public void Disconnect_ShutsdownWrites()
        {
            var queues = new Mock<IConnectionQueues>();

            var disconnector = new Disconnector(queues.Object, _ => new Mock<ITimeoutHelper>().Object);
            disconnector.Disconnect(TimeSpan.FromMinutes(1)).RunSynchronously();
           
            queues.Verify(q => q.ShutdownWrite(It.IsAny<TimeSpan>()),
                          "Writes were not shutdown on the connection queues.");
        }

        [Test(Description = "Disconnecting reads all remaining buffers until an empty buffer is reached.")]
        public void Disconnect_PurgesReadBuffers()
        {
            var fullbuffer = new Mock<IReadBuffer>();
            fullbuffer.SetupGet(fb => fb.IsEmpty).Returns(false);
            var emptybuffer = new Mock<IReadBuffer>();
            emptybuffer.SetupGet(eb => eb.IsEmpty).Returns(true);
            var results = new Queue<IReadBuffer>(new[]
                                                     {
                                                         fullbuffer.Object, 
                                                         fullbuffer.Object, 
                                                         emptybuffer.Object,
                                                         emptybuffer.Object
                                                     });

            var readbuffers = new Mock<IGuardedEnumerator<IReadBuffer>>();
            readbuffers.SetupGet(r => r.Current).Returns(results.Dequeue);
            readbuffers.Setup(r => r.MoveNext(It.IsAny<TimeSpan>()))
                .Returns(Task.Factory.StartNew(() => true));

            var disconnector = new Disconnector(CreateQueues(readbuffers.Object),
                                                _ => new Mock<ITimeoutHelper>().Object);
            disconnector.Disconnect(TimeSpan.FromMinutes(1)).RunSynchronously();
            
            readbuffers.Verify(r => r.MoveNext(It.IsAny<TimeSpan>()), Times.Once(),
                          "Move next call more than once or not at all.");
        }

        private static IConnectionQueues CreateQueues(IGuardedEnumerator<IReadBuffer> readBuffers = null)
        {
            readBuffers = readBuffers ??
                new Mock<IGuardedEnumerator<IReadBuffer>> {DefaultValue = DefaultValue.Mock}.Object;
            var writebuffers = new Mock<IGuardedEnumerator<IWriteBuffer>> {DefaultValue = DefaultValue.Mock};
            writebuffers.Setup(w => w.MoveNext(It.IsAny<TimeSpan>())).Returns(Task.Factory.StartNew(() => true));
            return new ConnectionQueues(writebuffers.Object, readBuffers);
        }
    }
}